﻿/*  ----------------------------------------------------------------------------
 *  SysServer
 *  ----------------------------------------------------------------------------
 *  File:       BoundedTStack`1.cs
 *  Creater:     SEEN\MistSeen
 *  ----------------------------------------------------------------------------
 *  $Date: 2010-09-06 10:36:01 +0000 (Mon, 06 Sep 2010) $
 *  $Rev: 15 $
 *  $Author: ScorDeity $
 *  $URL: http://sysserver.googlecode.com/svn/trunk/src/SysServer/Collections/Generic/BoundedTStack%601.cs $
 *  $Id: BoundedTStack`1.cs 15 2010-09-06 10:36:01Z ScorDeity $
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace SysServer.Collections.Generic
{
    public class BoundedTStack<T>
    {
        private TStackItem<T> _Head;
        private Semaphore _FreeSlots;
        private Semaphore _OccupiedSlots;


        public BoundedTStack()
        {
            this._Head = new TStackItem<T>();
            this._FreeSlots = new Semaphore(0, int.MaxValue);
            this._OccupiedSlots = new Semaphore(0, int.MaxValue);
        }
        public BoundedTStack(int itemCapacity)
        {
            if (itemCapacity < 1)
            {
                throw new ArgumentOutOfRangeException("capacity must be greater than 0");
            }
            this._Head = new TStackItem<T>();
            this._FreeSlots = new Semaphore(itemCapacity, itemCapacity);
            this._OccupiedSlots = new Semaphore(0, itemCapacity);
        }

        public void Push(T data)
        {
            TStackItem<T> newNode = new TStackItem<T>();
            newNode.Data = data;
            if (this._FreeSlots != null)
            {
                this._FreeSlots.WaitOne();
            }
            do
            {
                newNode.Next = this._Head.Next;
            } while (Interlocked.CompareExchange<TStackItem<T>>(ref this._Head.Next, newNode, newNode.Next) != newNode.Next);
            this._OccupiedSlots.Release();
        }
        public T Pop()
        {
            TStackItem<T> tempNode;
            do
            {
                tempNode = this._Head.Next;
                if (tempNode == null)
                {
                    this._OccupiedSlots.WaitOne();
                    continue;
                }
            } while (Interlocked.CompareExchange<TStackItem<T>>(ref this._Head.Next,tempNode.Next,tempNode)!=tempNode);

            if (this._FreeSlots != null)
            {
                this._FreeSlots.Release();
            }
            return tempNode.Data;
        }
    }
}